/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.bemanager.expression;

import com.inspur.edp.bef.bemanager.expression.entity.ExpressionEntitySet;
import com.inspur.edp.bef.bemanager.expression.entity.ExpressionEntityType;
import com.inspur.edp.bef.bemanager.expression.entity.ExpressionProperty;
import com.inspur.edp.bef.bemanager.expression.entity.ExpressionSchema;
import com.inspur.edp.bef.bemanager.expression.entity.NavigationProperty;
import com.inspur.edp.bef.bemanager.expression.entity.NavigationType;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.designtime.api.util.MetadataUtil;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.das.commonmodel.entity.GspCommonModel;
import com.inspur.edp.das.commonmodel.entity.GspCommonObject;
import com.inspur.edp.das.commonmodel.entity.object.GspCommonObjectType;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.service.MetadataRTService;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class BefExpressionAdapter {

  private final HashMap<String, ExpressionEntityType> udtTypeMap = new HashMap<>();

  public ExpressionSchema convertCommonModel(GspCommonModel model) {
    ExpressionSchema schema = new ExpressionSchema();
    List<ExpressionEntityType> types = new ArrayList<>();
    ExpressionEntityType mainObjType = getCommonObjectEntityType(model.getMainObject(), types);
    schema.getEntityTypes().add(mainObjType);
    schema.getEntityTypes().addAll(types);

    model.getAllObjectList().forEach(item -> {
      if (item.getObjectType() == GspCommonObjectType.ChildObject) {
        ExpressionEntitySet set = new ExpressionEntitySet();
        set.setName(item.getCode() + "s");
        set.setEntityType(item.getCode());
        schema.getEntitySets().add(set);
      }
    });
    return schema;
  }

  private ExpressionEntityType getCommonObjectEntityType(GspCommonObject obj,
      List<ExpressionEntityType> entityTypes) {
    ExpressionEntityType type = new ExpressionEntityType();
    type.setId(obj.getId());
    type.setKey(obj.getCode());
    type.setName(obj.getCode());
    type.setDescription(obj.getCode());

    // 字段集合
    obj.getContainElements().forEach(item -> {
      if (!item.getIsUdt() && item.getObjectType() != GspElementObjectType.Association) {
        type.getProperties().add(convertNormalElement(item, entityTypes, obj));
      } else {
        type.getNavigationProperties().add(getNavigationPropertyElement(item, entityTypes, obj));
      }

      //处理：将某个属性设置为关联之后，额外设置普通属性
      if ((item.getObjectType() == GspElementObjectType.Association
          && item.getChildAssociations().size() > 0 && ((GspAssociation) item.getChildAssociations()
          .get(0)).isKeepAssoPropertyForExpression())) {
        type.getProperties().add(convertNormalElement(item, entityTypes, obj));
      }

    });
    // 子表集合
    obj.getContainChildObjects().forEach(commonObject -> {
      NavigationProperty childProp = new NavigationProperty();
      childProp.setNavigationId(commonObject.getID());
      childProp.setDescription(commonObject.getName());
      childProp.setName(commonObject.getCode() + "s");
      childProp.setType(NavigationType.EntitySet);

      type.getNavigationProperties().add(childProp);
      // 类型集合
      entityTypes.add(this.getCommonObjectEntityType((GspCommonObject) commonObject, entityTypes));
    });
    return type;
  }
//
//  private ExpressionProperty convertElement(IGspCommonField element,
//      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject) {
//    if (element.getIsUdt()) {
//      return this.convertUdtElement(element, entityTypes, belongObject);
//    }
//    if (element.getObjectType() == GspElementObjectType.Association) {
//      return this.convertAssoElement(element, entityTypes, belongObject);
//    }
//    return this.convertNormalElement(element, entityTypes, belongObject);
//  }

  private NavigationProperty getNavigationPropertyElement(IGspCommonField field,
      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject) {
    if (field.getIsUdt()) {
      return convertUdtElement(field, entityTypes, belongObject);
    }
    if (field.getObjectType() == GspElementObjectType.Association) {
      return convertAssoElement(field, entityTypes, belongObject, null);
    }
    throw new RuntimeException("错误调用getNavigationPropertyElement");
  }

  private ExpressionProperty convertNormalElement(IGspCommonField field,
      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject) {
    ExpressionProperty property = new ExpressionProperty();
    property.setName(this.getCamelName(field.getLabelID()));
    property.setType(field.getMDataType().toString());
    property.setDescription(field.getName());
    return property;
  }

  private String getCamelName(String name) {
    return StringUtils.toCamelCase(name);
  }

  // region udt
  private NavigationProperty convertUdtElement(IGspCommonField field,
      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject) {
    NavigationProperty property = new NavigationProperty();
    // 使用udt类型Id
    property.setNavigationId(field.getID());
    property.setName(this.getCamelName(field.getLabelID()));
    ExpressionEntityType udtType = getUdtType(field, entityTypes, belongObject).clone();
    // udt类型Id
    udtType.setId(field.getID());
    udtType.setKey(field.getID());
    udtType.setName(this.getCamelName(field.getLabelID()));
    udtType.setDescription(field.getName());
    entityTypes.add(udtType);
    property.setType(NavigationType.EntityType);
    return property;
  }

  private ExpressionEntityType getUdtType(IGspCommonField field,
      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject) {
    // 字典包含
    if (udtTypeMap.containsKey(field.getUdtID())) {
      ExpressionEntityType type = udtTypeMap.get(field.getUdtID());
      return type;
    }
//    UnifiedDataTypeDef udt = loadUdt(field.getUdtID());
//    ExpressionEntityType udtType = new ExpressionEntityType();
//
//    // 添加复杂属性
//    NavigationProperty prop = new NavigationProperty();
//    // UDT属性名称=UDT编号
//    prop.setName(udt.getCode());
//    prop.setDescription(udt.getName());
//    prop.setType(NavigationType.EntityType);
//    udtType.getNavigationProperties().add(prop);

    // 添加复杂类型
    ExpressionEntityType type = getUdtElementType(field, entityTypes, belongObject);
//    entityTypes.add(type);

//    // udtType加入类型
//    entityTypes.add(udtType);
    // 添加到字典
    udtTypeMap.put(field.getUdtID(), type);
    return type;
  }

  /**
   * UDT类型
   */
  private ExpressionEntityType getUdtElementType(IGspCommonField field,
      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject) {
    UnifiedDataTypeDef udt = loadUdt(field.getUdtID());
    if (udt instanceof SimpleDataTypeDef) {
      return getSimpleUdtElementType(field, entityTypes, belongObject, (SimpleDataTypeDef) udt);
    } else {
      return getComplexUdtElementType(field, entityTypes, belongObject, (ComplexDataTypeDef) udt);
    }
  }

  private ExpressionEntityType getSimpleUdtElementType(IGspCommonField field,
      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject,
      SimpleDataTypeDef sUdt) {
    ExpressionEntityType udtType = new ExpressionEntityType();
    if (sUdt.getObjectType() == GspElementObjectType.Association) {
      //containRefElementNotFromAssoUdt方法各种抛异常，此处增加判断
      if (field.getChildAssociations() != null && field.getChildAssociations().size() > 0 && field
          .containRefElementNotFromAssoUdt()) {
        ArrayList<IGspCommonField> fields = new ArrayList<>();
        for (IGspCommonField refField : field.getChildAssociations().get(0)
            .getRefElementCollection()) {
          if (!refField.getIsFromAssoUdt()) {
            fields.add(refField);
          }
        }
        NavigationProperty prop = convertAssoElement(sUdt.getContainElements().get(0), entityTypes,
            belongObject, fields);
        udtType.getNavigationProperties().add(prop);

      } else {
        NavigationProperty prop = convertAssoElement(sUdt.getContainElements().get(0), entityTypes,
            belongObject, null);
        udtType.getNavigationProperties().add(prop);
      }
//
////      // 必然有一个普通属性：同UDT编号
////      udtType.getProperties().add(convertNormalElement(field, entityTypes, belongObject));
////
////      // 关联UDT的带出属性
////      sUdt.getChildAssociations().get(0).getRefElementCollection().forEach(refElement -> {
////        if (refElement.getIsUdt()
////            || refElement.getObjectType() == GspElementObjectType.Association) {
////          udtType.getNavigationProperties()
////              .add(getNavigationPropertyElement(refElement, entityTypes, belongObject));
////        } else {
////          udtType.getProperties().add(convertNormalElement(refElement, entityTypes, belongObject));
////        }
////      });
    } else {
      ExpressionProperty prop = convertNormalElement(sUdt.getContainElements().get(0), entityTypes,
          belongObject);
      udtType.getProperties().add(prop);
    }
    // UDT类型名称=UDT编号
    udtType.setName(this.getCamelName(field.getLabelID()));
    udtType.setKey(field.getID());
    udtType.setDescription(field.getName());
    return udtType;
  }

  private ExpressionEntityType getComplexUdtElementType(IGspCommonField field,
      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject,
      ComplexDataTypeDef cUdt) {
    ExpressionEntityType udtType = new ExpressionEntityType();
    cUdt.getContainElements().forEach(element -> {
      udtType.getProperties().add(convertNormalElement(element, entityTypes, belongObject));
    });
    udtType.setName(this.getCamelName(cUdt.getCode()));
    udtType.setKey(cUdt.getCode());
    udtType.setDescription(cUdt.getName());
    return udtType;
  }

//  private String getUdtTypeName(IGspCommonField field, GspCommonObject belongObject,
//      UnifiedDataTypeDef udt) {
//    return belongObject.getCode() + field.getLabelID() + udt.getCode();
//  }
  // endregion

  // region asso
  private NavigationProperty convertAssoElement(IGspCommonField field,
      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject,
      ArrayList<IGspCommonField> extendFields) {
//    NavigationProperty property = new NavigationProperty();
//    // 使用关联类型Id
//    property.setNavigationId(field.getID());
//    property.setType(NavigationType.EntityType);
//    property.setName(this.getCamelName(field.getLabelID()));
//
//    ExpressionEntityType assoType = getAssoElementType(field, entityTypes, belongObject, extendFields).clone();
//    // 赋值关联类型id
//    assoType.setId(field.getID());
//    assoType.setKey(field.getID());
//    assoType.setName(this.getCamelName(field.getLabelID()));
//    assoType.setDescription(field.getName());
//    entityTypes.add(assoType);
//    return property;
    NavigationProperty property = new NavigationProperty();
    property.setName(this.getCamelName(field.getLabelID()));
    property.setType(NavigationType.EntityType);
    ExpressionEntityType assoType = this
        .getAssoElementType(field, entityTypes, belongObject, extendFields);
    if (field.getChildAssociations().size() > 0 && ((GspAssociation) field.getChildAssociations()
        .get(0)).isKeepAssoPropertyForExpression()) {
      //设置___AssoExt
      property.setName(this.getCamelName(field.getCode()) + "___AssoExt");
      property.setNavigationId(this.getCamelName(field.getCode()) + "___AssoExt");
      assoType.setId(this.getCamelName(field.getCode()) + "___AssoExt");
      assoType.setName(this.getCamelName(field.getCode()) + "___AssoExt");
    } else {
      property.setName(this.getCamelName(field.getCode()));
      property.setNavigationId(this.getCamelName(field.getCode()));
      assoType.setId(this.getCamelName(field.getCode()));
      assoType.setName(this.getCamelName(field.getCode()));
    }
    assoType.setKey(field.getID());
    entityTypes.add(assoType);
    return property;
  }

  private ExpressionEntityType getAssoElementType(IGspCommonField field,
      List<ExpressionEntityType> entityTypes, GspCommonObject belongObject,
      ArrayList<IGspCommonField> extendFields) {
    ExpressionEntityType assoType = new ExpressionEntityType();

    assoType.setName(this.getCamelName(field.getLabelID()));
    assoType.setKey(field.getID());
    assoType.setDescription(field.getName());

    // 与当前关联字段同名普通属性
    assoType.getProperties().add(convertNormalElement(field, entityTypes, belongObject));
    //todo: 临时兼容-关联带出字段属性为关联类型时，关联未带出,实际上，应该到对应BE上去取关联
    if (field.getChildAssociations().size() == 0) {
      return assoType;
    }
    // 关联UDT的带出属性
    field.getChildAssociations().get(0).getRefElementCollection().forEach(refElement -> {
      if (refElement.getIsUdt() || refElement.getObjectType() == GspElementObjectType.Association) {
        assoType.getNavigationProperties()
            .add(getNavigationPropertyElement(refElement, entityTypes, belongObject));
      } else {
        assoType.getProperties().add(convertNormalElement(refElement, entityTypes, belongObject));
      }
    });

    if (extendFields != null && extendFields.size() > 0) {
      extendFields.forEach(refElement -> {
        if (refElement.getIsUdt()
            || refElement.getObjectType() == GspElementObjectType.Association) {
          assoType.getNavigationProperties()
              .add(getNavigationPropertyElement(refElement, entityTypes, belongObject));
        } else {
          assoType.getProperties().add(convertNormalElement(refElement, entityTypes, belongObject));
        }
      });
    }

    return assoType;
  }

  /**
   * 关联类型名称
   */
  private String getAssociationTypeName(IGspCommonField field, IGspCommonObject belongObject) {
    return belongObject.getCode() + field.getLabelID() + "Info";
  }

  // endregion

  private GspBusinessEntity loadBe(String beId) {
    return (GspBusinessEntity) getMetadata(beId).getContent();
  }

  private UnifiedDataTypeDef loadUdt(String udtId) {
    return (UnifiedDataTypeDef) getMetadata(udtId).getContent();
  }

  private GspMetadata getMetadata(String beId) {
    if (beId == null || beId.isEmpty()) {
      throw new RuntimeException("请传入有效beId.");
    }
    MetadataRTService service = (MetadataRTService) SpringBeanUtils
        .getBean(MetadataRTService.class);
    GspMetadata meta = service.getMetadata(beId);
    if (meta == null) {
      MetadataUtil.getCustomMetadata(beId);
    }
    if (meta == null) {
      throw new RuntimeException("根据BeID'" + beId + "'，使用CustomizationService未获取到元数据定义");
    }
    return meta;
  }
}
